<?php

/**
 * 模板类
 *
 */
class TagTemplate{
    public $oldFix;
    public $newFix;
    public $oldPath;
    public $newPath;
    public $oldFilePath;
    public $newFilePath;
    public $tableFix;
    public $tagFlagStr;
    public $tagConstant;

    //定义静态变量保存当前类的实例
    private static $instance;

    //防止在外部实例化
    private function __construct($oldPath = '', $newPath = '', $oldFix='.html', $newFix='.php', $tableFix = 'zhl_'){
        $this->oldFix = $oldFix;
        $this->newFix = $newFix;
        $this->oldPath = $oldPath;
        $this->newPath = $newPath;
        $this->tableFix = $tableFix;
        $this->tagFlagStr = 'sql'; // zhl、sql
        $this->tagConstant = array(
            'SYS_HOST',
            'SYS_LINK',
            'SYS_URL',
            'SYS_URL_FIX',
            'SYS_PATH',
            'V_CSSJS',
            'OSS_HOST'
            );
    }
    //防止在外部克隆
    private function __clone(){

    }

    //通过静态公有的方法获取这个类的实例
    public static function getInstance($oldPath = '', $newPath = '', $oldFix='.html', $newFix='.php', $tableFix = 'zhl_'){
        //当前对象不属于当前例就实例化
        if (!self::$instance instanceof self) {
            self::$instance = new self($oldPath, $newPath, $oldFix, $newFix, $tableFix);
        }
        return self::$instance;
    }

    /**
     * Notes: 模版内容写入新文件
     */
    public function turn($fileName){
        // 老文件全路径
        $this->oldFilePath = $this->oldPath.'/'.$fileName;
        // 判断模版文件是否存在
        if(!file_exists($this->oldFilePath)){
            $this->errMsg('模版文件不存在');
        }

        // 新文件全路径
        $this->newFilePath = $this->newPath.'/'.str_replace($this->oldFix, $this->newFix, $fileName);

        //新文件不存在 且 模版文件创建时间 大于 新文件创建时间 则更新
        if(!file_exists($this->newFilePath) || filemtime($this->oldFilePath) > filemtime($this->newFilePath)){
            $content = $this->openFileInfo($this->oldFilePath);
            $content = $this->templateHtml($content);
            $this->writeFile($this->newFilePath, $content);
        }
    }

    /**
     * Notes: 打开文件获得内容
     */
    private function openFileInfo($filename){
        $fp = @fopen($filename, "r");
        $txt = @fread($fp, filesize($filename));
        @fclose($fp);
        return $txt;
    }

    /**
     * Notes: 内容写入文件
     */
    private function writeFile($filePath, $content){
        $newfp = @fopen($filePath,"w");
        @fwrite($newfp, $content);
        @fclose($newfp);
        return true;
    }

    /**
     * Notes: 模版标签匹配和解析
     */
    private function templateHtml($content){
        $content = $this->getTagInclude($content);
        $content = $this->getTagSYSConstEcho($content);
        $content = $this->getTagEcho($content);
        $content = $this->getTagGive($content);
        $content = $this->getTagConvert($content);
        $content = $this->getTagFunction($content);
        $content = $this->getTagFunctionEcho($content);
        $content = $this->getTagIfElse($content);
        $content = $this->getTagForeach($content);
        $content = $this->getTagSql($content);
        return $content;
    }

    /**
     * Notes: include标签匹配
     */
    private function getTagInclude($content){
        preg_match_all('/\{include=\"(.*?)\"\}/si',$content,$i);
        foreach($i[0] as $k=>$v){
            $content = str_ireplace($v, $this->trimTagInclude(strtolower($i[1][$k])), $content);
        }
        return $content;
    }

    /**
     * Notes: include标签解析
     * Desc: 打开文件获取内容回调解析标签
     */
    private function trimTagInclude($filename){
        $filename = $this->oldPath.'/'.trim($filename);
        $txt = $this->openFileInfo($filename);
        return $this->templateHtml($txt);
    }

    /**
     * Notes:系统常量标签匹配解析
     */
    private function getTagSYSConstEcho($content){
        foreach($this->tagConstant as $t){
            //$content = $this->tagConstSet($t, $content);
            preg_match_all('/\{'.$t.'}/', $content, $h);
            foreach($h[0] as $k=>$v){
                $content = str_ireplace($v, '<?php echo '.$t.'; ?>', $content);
            }
        }
        return $content;
    }

    /**
     * Notes:变量输出标签匹配解析
     */
    private function getTagEcho($content){
        preg_match_all('/\{\$(.*?)\}/si', $content, $i);
        foreach($i[0] as $k=>$v){
            $val = $i[1][$k];
            $val = str_replace('"', "'", $val);
            if(strpos($val,"'") === false){
                if(strpos($val,".") !== false){
                    $vs = explode('.',$val);
                    $val = '';
                    foreach($vs as $vk=>$vv){
                        $val .= !$val ? $vv : "['$vv']";
                    }
                    //$val = str_replace('.',"['", $val)."']";
                }
            }
            $content = str_ireplace($v, '<?php echo $'.$val.'; ?>', $content);
        }
        return $content;
    }

    /**
     * Notes:变量赋值标签匹配解析
     */
    private function getTagGive($content){
        preg_match_all('/\{\|\$(.*?)\}/si', $content, $i);
        foreach($i[0] as $k=>$v){
            $val = $i[1][$k];
            if(strpos($val,"'") === false){
                if(strpos($val,".") !== false){
                    $vs = explode('.',$val);
                    $val = '';
                    foreach($vs as $vk=>$vv){
                        $val .= !$val ? $vv : "['$vv']";
                    }
                    //$val = str_replace('.',"['", $val)."']";
                }
            }
            $content = str_ireplace($v, '<?php $'.$val.'; ?>', $content);
        }
        return $content;
    }

    /**
     * Notes:变量标签匹配解析
     */
    private function getTagConvert($content){
        preg_match_all('/\$(?![0-9]+$)(?![a-zA-Z]+$)[0-9a-zA-Z]{1,}\.(?![0-9]+$)(?![a-zA-Z]+$)[0-9a-zA-Z]{1,}/si', $content, $i);
        if(isset($i[0])){
            $vals = array();
            foreach($i[0] as $v){
                $vals[] = str_replace(".", "['", $v)."']";
            }
            $content = str_ireplace($i[0], $vals, $content);
        }
        return $content;
    }

    /**
     * Notes:方法标签匹配解析
     */
    private function getTagFunction($content){
        preg_match_all('/\{\|fun (.*?)\}/si', $content, $i);
        foreach($i[0] as $k=>$v){
            $content = str_ireplace($v, '<?php '.$i[1][$k].'; ?>', $content);
        }
        return $content;
    }

    /**
     * Notes:方法标签匹配解析
     */
    private function getTagFunctionEcho($content){
        preg_match_all('/\{fun (.*?)\}/si', $content, $i);
        foreach($i[0] as $k=>$v){
            $content = str_ireplace($v, '<?php echo '.$i[1][$k].'; ?>', $content);
        }
        return $content;
    }

    /**
     * Notes:if标签匹配解析
     */
    private function getTagIfElse($content){
        preg_match_all('/\{if(.*?)\}/si', $content, $i);
        $this->tagCheck($content, '{/if}', $i[0],'if');
        foreach($i[0] as $k=>$v){
            $content = str_ireplace($v,'<?php if'.$i[1][$k].'{ ?>', $content);
        }
        preg_match_all('/\{elseif(.*?)\}/si', $content, $i);
        //$this->tagCheck($content, '{/if}', $i[0], 'if');
        foreach($i[0] as $k=>$v){
            $content = str_ireplace($v, '<?php }elseif'.$i[1][$k].'{ ?>', $content);
        }
        preg_match_all('/\{else if(.*?)\}/si', $content, $i);
        //$this->tagCheck($content, '{/if}', $i[0], 'if');
        foreach($i[0] as $k=>$v){
            $content = str_ireplace($v, '<?php }else if'.$i[1][$k].'{ ?>', $content);
        }
        $content = str_ireplace('{else}', '<?php }else{ ?>', $content);
        return str_ireplace('{/if}', '<?php } ?>', $content);
    }

    /**
     * Notes:foreach标签匹配解析
     */
    private function getTagForeach($content){
        preg_match_all('/\{foreach(.*?)\}/si', $content, $i);
        $this->tagCheck($content,  '{/foreach}', $i[0], 'foreach');
        foreach($i[0] as $k=>$v){
            $content = str_ireplace($v, '<?php foreach('.$i[1][$k].'){ ?>', $content);
        }
        return str_ireplace('{/foreach}', '<?php } ?>', $content);
    }

    /**
     * Notes: sql标签匹配
     */
    private function getTagSql($content){
        preg_match_all('/\{'.$this->tagFlagStr.' (.*?)\}/si', $content, $i);
        $this->tagCheck($content, '{/'.$this->tagFlagStr.'}', $i[0], $this->tagFlagStr);
        foreach($i[0] as $k=>$v){
            $content = str_ireplace($v, $this->trimTagSql(strtolower($i[1][$k])), $content);
        }
        return str_ireplace('{/'.$this->tagFlagStr.'}', '<?php } ?>', $content);
    }

    /**
     * Notes:标签验证
     * Desc:
     */
    private function tagCheck($content, $tag, $tagRes, $msg){
        if(substr_count($content, $tag) != count($tagRes)){
            $msgInfo = $this->oldFilePath.'模板中存在不完整'.$msg.'标签，请检查是否遗漏{'.$msg.'}开始或结束符';
            $this->errMsg($msgInfo);
        }
    }

    /**
     * Notes:错误提示
     */
    private function errMsg($msgInfo){
        exit('<meta http-equiv="Content-Type" content="text/html; charset=utf-8">'.$msgInfo);
    }

    /**
     * Notes:sql语句表连接
     * User: ZhuHaili
     * Date: 2023/3/9
     */
    private function sqlJoin($tagArr){
        // 所有join类型和表
        $joinArr = array();
        foreach($tagArr as $k=>$v){
            if(in_array($k, array('join','leftjoin','rightjoin','innerjoin'))){
                $joinTypeArr = explode('join', $k);
                $joinType = $joinTypeArr[0] ? $joinTypeArr[0].' join' : 'left join';
                foreach(explode(',', $v) as $joinTable){
                    $joinArr[] = $joinType.' '.$this->tableFix.$joinTable;
                }
            }
        }
        // join on
        $onArr = array();
        if(isset($tagArr['on'])){
            foreach(explode(',', $tagArr['on']) as $ons){
                $ons = str_replace(':','=', $ons);
                $ons = str_replace('|',' or ', $ons);
                $ons = str_replace('&',' and ', $ons);
                $onArr[] = $ons;
            }
        }
        if(count($joinArr) != count($onArr)){
            $this->errMsg('表连接和on个数不匹配');
        }

        $join = '';
        foreach($joinArr as $k=>$v){
            $join .= " $v on ".$onArr[$k];
        }
        return $join;
    }

    /**
     * Notes: where大于等于小于等于公共
     * User: ZhuHaili
     * Date: 2023/3/9
     */
    private function whereGtLt($tagArr, $strFlag='', $char=''){
        $w = '';
        foreach($tagArr as $k=>$v){
            if($v){
                if(strpos($k,$strFlag) !== false){
                    $fieldArr = explode($strFlag, $k);
                    $field = end($fieldArr);
                    $w .= " and $field $char $v";
                }
            }
        }
        return $w;
    }

    /**
     * Notes:where条件and
     * User: ZhuHaili
     * Date: 2023/3/9
     */
    private function whereAnd($tagArr){
        $and = '';
        foreach($tagArr as $k=>$v){
            if($v){
                if(strpos($k,"and.") !== false){
                    $fieldArr = explode('and.', $k);
                    $field = end($fieldArr);
                    $flag = substr($v,0,1) != '!' ? '=' : '!=';
                    $val = $flag == '=' ? $v : str_replace('!','', $v);
                    $and .= " and $field $flag $val";
                }
            }
        }
        return $and;
    }

    /**
     * Notes:where条件or
     * User: ZhuHaili
     * Date: 2023/3/9
     */
    private function whereOr($tagArr){
        $orArr = array();
        foreach($tagArr as $k=>$v){
            if($v){
                if(strpos($k,"or.") !== false){
                    $fieldArr = explode('or.', $k);
                    $field = end($fieldArr);
                    $flag = substr($v,0,1) != '!' ? '=' : '!=';
                    $val = $flag == '=' ? $v : str_replace('!','', $v);
                    $orArr[]= "$field $flag $val";
                }
            }
        }
        $or = '';
        if($orArr){
            $or = 'and ('.implode(' or ', $orArr).')';
        }
        return $or;
    }

    /**
     * Notes:where条件between
     * User: ZhuHaili
     * Date: 2023/3/9
     */
    private function whereBetween($tagArr){
        $between = '';
        foreach($tagArr as $k=>$v){
            if($v){
                if(strpos($k,"between.") !== false){
                    $fieldArr = explode('between.', $k);
                    $field = end($fieldArr);
                    $between .= " and $field between ".str_replace(',',' and ', $v);
                }
            }
        }
        return $between;
    }

    /**
     * Notes:where条件in
     * User: ZhuHaili
     * Date: 2023/3/9
     */
    private function whereIn($tagArr){
        $in = '';
        foreach($tagArr as $k=>$v){
            if($v){
                if(strpos($k,"in.") !== false){
                    $fieldArr = explode('in.', $k);
                    $field = end($fieldArr);
                    $in .= " and $field in('".str_replace(',',"','", $v)."')";
                }
            }
        }
        return $in;
    }

    /**
     * Notes:where条件in
     * User: ZhuHaili
     * Date: 2023/3/9
     */
    private function whereNotIn($tagArr){
        $notin = '';
        foreach($tagArr as $k=>$v){
            if($v){
                if(strpos($k,"notin.") !== false){
                    $fieldArr = explode('notin.', $k);
                    $field = end($fieldArr);
                    $notin .= " and $field not in('".str_replace(',',"','", $v)."')";
                }
            }
        }
        return $notin;
    }

    /**
     * Notes:where条件like
     * User: ZhuHaili
     * Date: 2023/3/9
     */
    private function whereLike($tagArr){
        $like = '';
        foreach($tagArr as $k=>$v){
            if($v){
                if(strpos($k,"like.") !== false){
                    $fieldArr = explode('like.', $k);
                    $field = end($fieldArr);
                    $like .= " and $field like '$v'";
                }
            }
        }
        return $like;
    }

    /**
     * Notes:where条件locate
     * User: ZhuHaili
     * Date: 2023/3/9
     */
    private function whereLocate($tagArr){
        $locate = '';
        foreach($tagArr as $k=>$v){
            if($v){
                if(strpos($k,"locate.") !== false){
                    $fieldArr = explode('locate.', $k);
                    $field = end($fieldArr);
                    $locate .= " and locate ($v,$field) > 0";
                }
            }
        }
        return $locate;
    }

    /**
     * Notes:sql语句groupby
     * User: ZhuHaili
     * Date: 2023/3/9
     */
    private function sqlGroupBy($tagArr){
        $group = '';
        if(isset($tagArr['group'])){
            if($tagArr['group']){
                $group = " group by ".$tagArr['group'];
            }
        }
        return $group;
    }

    /**
     * Notes:sql语句having
     * User: ZhuHaili
     * Date: 2023/3/9
     */
    private function sqlHaving($tagArr){
        $having = '';
        if(isset($tagArr['having'])){
            if($tagArr['having']){
                $having = " having ".str_replace(',',' and ', rtrim($tagArr['having'], ','));
            }
        }
        return $having;
    }

    /**
     * Notes:sql语句orderBy
     * User: ZhuHaili
     * Date: 2023/3/9
     */
    private function sqlOrderBy($tagArr){
        $field = '';
        foreach($tagArr as $k=>$v){
            if($v){
                if(strpos($k,"order.") !== false){
                    $fieldArr = explode('order.', $k);
                    $field .= $field ? ','.end($fieldArr)." $v" : end($fieldArr)." $v";
                }
            }
        }
        $order = $field ? " order by $field" : '';
        return $order;
    }

    /**
     * Notes:sql语句limit
     * User: ZhuHaili
     * Date: 2023/3/9
     */
    private function sqlLimit($tagArr){
        $limit = '';
        if(isset($tagArr['limit'])){
            if($tagArr['limit']){
                $limit = " limit ".$tagArr['limit'];
            }
        }
        return $limit;
    }

    /**
     * Notes:sql标签解析
     */
    private function trimTagSql($content){
        // 找出sql包含的标签内容生成一个数组
        preg_match_all('/.*?(\s*.*?=.*?[\"|\'].*?[\"|\']\s).*?/si', ' '.$content.' ', $i);
        // 数组信息找出属性和值
        $tagArr = array();
        foreach($i[1] as $v){
            $v = trim($v); // 去除换行回车空格等
            $v = strtolower($v); // 全部转小写
            $v = str_replace('"','', $v); // 去除双引号
            $t = explode('=', $v); // 根据等号切割字符串成数组
            $tagArr = array_merge($tagArr, array(trim($t[0]) => (isset($t[1]) ? trim($t[1]) : ''))); // key=>val重组
        }

        $tagArr['tablefix'] = $this->tableFix;

        // 判断from参数（必须指定）
        if(!isset($tagArr['from'])){
            $this->errMsg('请指定要查询的表');
        }
        if(!$tagArr['from']){
            $this->errMsg('请指定要查询的表');
        }
        // from表
        $table = $this->tableFix.$tagArr['from'];
        // select字段
        $select = isset($tagArr['select']) ? $tagArr['select'] : '';
        $select = $select  ? rtrim($select, ',') : '*';
        // 表连接
        $join = $this->sqlJoin($tagArr);
        // where条件and
        $and = $this->whereAnd($tagArr);
        // where条件or
        $or = $this->whereOr($tagArr);
        // where条件大于
        $gt = $this->whereGtLt($tagArr, $strFlag='gt.', $char='>');
        // where条件小于
        $lt = $this->whereGtLt($tagArr, $strFlag='lt.', $char='<');
        // where条件大于等于
        $gts = $this->whereGtLt($tagArr, $strFlag='gts.', $char='>=');
        // where条件小于等于
        $lts = $this->whereGtLt($tagArr, $strFlag='lts.', $char='<=');
        // where条件between
        $between = $this->whereBetween($tagArr);
        // where条件in
        $in = $this->whereIn($tagArr);
        // where条件not in
        $notin = $this->whereNotIn($tagArr);
        // where条件like
        $like = $this->whereLike($tagArr);
        // where条件locate
        $locate = $this->whereLocate($tagArr);
        // group by
        $group = $this->sqlGroupBy($tagArr);
        // having
        $having = $this->sqlHaving($tagArr);
        // order by
        $order = $this->sqlOrderBy($tagArr);
        // limit
        $limit = $this->sqlLimit($tagArr);
        // 组装sql
        $sqlStr = "select $select from $table $join where 1=1 $and $or $gt $gts $lt $lts";
        $sqlStr .= "$between $in $notin $like $locate $group $having $order $limit";
        // 命名变量名
        $val = isset($tagArr['val']) && $tagArr['val'] ? $tagArr['val'] : 'v';
        // 分页相关
        $pageJob = '';
        $pageNo = isset($tagArr['page']) ? (intval($tagArr['page']) ? intval($tagArr['page']) : 1) : 0;
        if($pageNo){
            $limit = isset($tagArr['limit']) ? (intval($tagArr['limit']) ? intval($tagArr['limit']) : 10) : 10;
            $sqlStr2 = "select count(1) counts from $table $join where 1=1 $and $or $gt $gts $lt $lts";
            $sqlStr2 .= "$between $in $notin $like $locate $group $having ";

$pageJob = '$dataCountArr = $db->query("'.$sqlStr2.'", 1, 1);
$dataCount = isset($dataCountArr["counts"]) ? $dataCountArr["counts"] : 0;
$pageE = new ManagePages('.$pageNo.', $dataCount, '.$limit.');
$limitArr = $pageE->limitArr;
$limitStart = $limitArr[0];
$limitEnd = $limitArr[1];
$pageNo = '.$pageNo.';
$pagesArr = $pageE->show();
$pages = "";
foreach($pagesArr as $v){
    $pages .= $v;
}
';
            $sqlStr = "select $select from $table $join where 1=1 $and $or $gt $gts $lt $lts";
            $sqlStr .= "$between $in $notin $like $locate $group $having $order limit ".'$limitStart,$limitEnd';

        }

// 输入的内容
$content = '
<?php
$db = Model::conn("mysql");
'.$pageJob.'
$res = $db->query("'.$sqlStr.'", 1);
foreach($res as $k=>$'.$val.'){ 
    if($pageNo){
        $'.$val.'["pages"] = "";
        foreach($pagesArr as $pageInfoE){
            $'.$val.'["pages"] .= $pageInfoE;
        }
        $'.$val.'["pagesArr"] = $pagesArr;
    }
?>';

        return $content;
    }

}